package git.soulbgm.common;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author 贺瑞杰
 * @version V1.0
 * @date 2018-08-08 17:25
 * @description redis操作接口
 */
public interface RedisClient {

    /**
     * 查看key的剩余生存时间
     * 实现命令：ttl key
     *
     * @param key 键
     * @return 以秒为单位，返回给定key的剩余生存时间
     */
    long ttl(String key);

    /**
     * 设置过期时间（单位秒）
     * Redis实现命令：expire key 过期时间（单位秒）
     *
     * @param key     键
     * @param timeout 过期时间
     */
    boolean expire(String key, long timeout);

    /**
     * 递增或递减数值类型key的值  例如：num初始为1  执行此方法 incr(num,5) 那么此时num就为6  递减则为负数 原子性操作
     * 适合统计，高并发生成订单号，秒杀类的业务逻辑等使用
     * Redis实现命令：incr key  (注：没有直接加给定数值的命令  只能每次加1)
     *
     * @param key   键
     * @param delta 增加给定的数值
     * @return 递增或递减的值
     */
    long incr(String key, long delta);

    /**
     * 删除给定的key
     * Redis实现命令：del key
     *
     * @param key 多个不定数量的键
     * @return 删除成功或失败 成功为true 失败为false
     */
    boolean del(String... key);

    /**
     * 删除给定的key
     * Redis实现命令：del key
     *
     * @param list 多个key的集合
     * @return 删除成功或失败 成功为true 失败为false
     */
    boolean del(Collection<String> list);

    /**
     * 查找所有符合给定模式的key
     * Redis实现命令：keys 正则表达式   例如：keys na*
     *
     * @param pattern 正则表达式
     * @return 符合模式是所有key
     */
    Set<String> keys(String pattern);

    /**
     * 查找是否有此key
     * Redis实现命令：exists key
     *
     * @param key 键
     * @return 有返回true 没有返回false
     */
    boolean exists(String key);

    //TODO K-V 的操作

    /**
     * 设置一个key-value（将字符串值 value关联到 key）
     * Redis实现命令：set key value
     *
     * @param key   键
     * @param value 值
     */
    void set(String key, String value);

    /**
     * 设置一个key-value（将字符串值 value关联到 key）  序列化方式
     * Redis实现命令：set key value
     *
     * @param key   键
     * @param data  实例对象
     * @param clazz 实例对象的class
     */
    <T> void set(String key, T data, Class<T> clazz);

    /**
     * 设置key-value和超时时间（秒）
     * Redis实现命令：set key value ex 超时时间（单位秒）
     *
     * @param key     键
     * @param value   值
     * @param timeout 过期时间（秒）
     */
    void set(String key, String value, long timeout);

    /**
     * 设置key-value和超时时间（秒）
     * Redis实现命令：set key value ex 超时时间（单位秒）
     *
     * @param key      键
     * @param value    值
     * @param timeout  过期时间
     * @param timeUnit 时间单位
     */
    void set(String key, String value, long timeout, TimeUnit timeUnit);

    /**
     * 设置key-value和超时时间（秒）  序列化方式
     * Redis实现命令：set key value ex 超时时间（单位秒）
     *
     * @param key     键
     * @param data    实例对象
     * @param clazz   实例对象的class
     * @param timeout 过期时间（秒）
     */
    <T> void set(String key, T data, Class<T> clazz, long timeout);

    /**
     * setnx的工作方式与set完全一样，唯一的区别是，如果键已经存在返回false，则不执行任何操作。 setnx实际上意味着“如果不存在set”
     * Redis实现命令：setnx key value 返回1 set成功 返回0 set失败
     *
     * @param key   键
     * @param value 值
     */
    boolean setnx(String key, String value);

    /**
     * 这个命令类似于以下两个命令
     * set key value
     * expire key seconds  # 设置生存时间
     * 不同之处是，setnx 是一个原子性(atomic)操作，关联值和设置生存时间两个动作会在同一时间内完成，该命令在 Redis 用作缓存时，非常实用
     * key 已经存在时，setex 覆盖旧值
     * Redis实现命令：setex key value 过期时间（单位秒）
     *
     * @param key     键
     * @param value   值
     * @param timeout 过期时间（秒）
     */
    boolean setex(String key, String value, long timeout);

    /**
     * Redis 中实现锁的简单方法
     * Redis实现命令：set key value NX EX 过期时间（单位秒）
     *
     * @param key     键
     * @param value   值
     * @param timeout 过期时间（秒）
     * @return 如果服务器返回 OK ，那么这个客户端获得锁，如果服务器返回 NIL ，那么客户端获取锁失败，可以在稍后再重试。
     */
    String setNxEx(String key, String value, long timeout);

    /**
     * 返回 key所关联的字符串值
     * Redis实现命令：get key
     *
     * @param key 键
     * @return key对应的值
     */
    Object get(String key);

    /**
     * 返回 key所关联的字符串值 序列化方式
     * Redis实现命令：get key
     *
     * @param key   键
     * @param clazz 实例对象的class
     * @return key对应的实例对象
     */
    <T> T get(String key, Class<T> clazz);

    /**
     * 返回 key所关联的字符串值 并set进一个新值 属于一个原子性操作
     * Redis实现命令：getset key value		注：如果原本没有key则(nil)
     *
     * @param key   键
     * @param value 值
     * @return 上一次执行的值
     */
    Object getAndSet(String key, String value);

    //TODO Hash 的操作

    /**
     * 将哈希表 key中的域 field的值设为 value
     * Redis实现命令：hset key field value
     *
     * @param key   哈希表的key
     * @param field 哈希表中的key
     * @param value 哈希表中的key对于的value
     */
    void hset(String key, String field, Object value);

    /**
     * 将整个map放哈希表中
     *
     * @param key 哈希表的key
     * @param map Map
     */
    void hsetAll(String key, Map map);

    /**
     * 返回哈希表 key中，所有的field和value
     * Redis实现命令：hgetall key
     *
     * @param key
     * @return
     */
    <T> Map<String, T> hgetAll(String key, Class<T> clazz);

    /**
     * 返回哈希表 key中给定域 field的值
     * Redis实现命令：hget key field
     *
     * @param key   哈希表的key
     * @param field 哈希表中的key
     * @return 给定域 field的值
     */
    Object hget(String key, String field);

    /**
     * 返回哈希表 key中，所有的field和value
     * Redis实现命令：hgetall key
     *
     * @param key 键
     * @return 哈希表中全部的数据
     */
    Map<Object, Object> hgetAll(String key);

    /**
     * 删除哈希表key中的一个会多个指定域，不存在的域将被忽略
     * Redis实现命令：hdel key field [field ...]
     *
     * @param key   键
     * @param field 不定数量的哈希表中的key
     * @return 删除field的数量
     */
    long hdel(String key, Object... field);

    //TODO List 的操作

    /**
     * 将一个值 value插入到列表 key的头部
     * Redis实现命令：lpush key value
     *
     * @param key   键
     * @param value 值
     * @return 插入值之后列表的长度
     */
    long lpush(String key, String value);

    /**
     * 将一个值 value插入到列表 key的头部 序列化方式 保证该list中所有的数据属于同一个类
     * Redis实现命令：lpush key value
     *
     * @param key   键
     * @param data  实例对象
     * @param clazz 实例对象的class
     * @return 插入值之后列表的长度
     */
    <T> long lpush(String key, T data, Class<T> clazz);

    /**
     * 将一个值 value插入到列表 key的尾部
     * Redis实现命令：rpush key value
     *
     * @param key   键
     * @param value 值
     * @return 插入值之后列表的长度
     */
    long rpush(String key, String value);

    /**
     * 将一个值 value插入到列表 key的尾部 序列化方式 保证该list中所有的数据属于同一个类
     * Redis实现命令：rpush key value
     *
     * @param key   键
     * @param data  实例对象
     * @param clazz 实例对象的class
     * @return 插入值之后列表的长度
     */
    <T> long rpush(String key, T data, Class<T> clazz);

    /**
     * 获取从 开始位置到结束位置 的value
     * Redis实现命令：lrange key start end
     *
     * @param key   键
     * @param start 开始位置
     * @param end   结束位置
     * @return 开始位置到结束位置的值列表
     */
    List<String> lget(String key, long start, long end);

    /**
     * 获取从 开始位置到结束位置 的value  序列化方式 保证该list中所有的数据属于同一个类
     * Redis实现命令：lrange key start end
     *
     * @param key   键
     * @param start 开始位置
     * @param end   结束位置
     * @param clazz 实例对象的class
     * @return 开始位置到结束位置的实例对象列表
     */
    <T> List<T> lget(String key, long start, long end, Class<T> clazz);

    /**
     * 获取全部列表
     * Redis实现命令：lrange key 0 -1
     *
     * @param key 键
     * @return 全部的值列表
     */
    List<String> lget(String key);

    /**
     * 获取全部列表 序列化方式 保证该list中所有的数据属于同一个类
     * Redis实现命令：lrange key 0 -1
     *
     * @param key   键
     * @param clazz 实例对象的class
     * @return 全部的实例对象列表
     */
    <T> List<T> lget(String key, Class<T> clazz);

    /**
     * 获得列表的大小
     *
     * @param key 键
     * @return 列表大小
     */
    long listSize(String key);

    //TODO Set 操作

    /**
     * 添加Set
     * Redis实现命令：sadd key value1 value2 ...
     *
     * @param key   键
     * @param value 不定数量的值
     * @return 插入值之后列表的长度
     */
    long setAdd(String key, String... value);

    /**
     * 添加Set
     * Redis实现命令：sadd key value1 value2 ...
     *
     * @param key   键
     * @param clazz 实例对象的class
     * @param value 实例对象
     * @return 插入值之后列表的长度
     */
    <T> long setAdd(String key, Class<T> clazz, T value);

    /**
     * 删除Set值
     * Redis实现命令：srem key value1 value2 ...
     *
     * @param key   键
     * @param value 不定数量的值
     * @return 删除之后列表的长度
     */
    long setRemove(String key, Object... value);

    /**
     * 获得Set的所有值
     * Redis实现命令：smembers key
     *
     * @param key 键
     * @return 全部的值列表
     */
    Set<String> setAll(String key);

    /**
     * 获得Set的所有值
     * Redis实现命令：smembers key
     *
     * @param key   键
     * @param clazz 实例对象的class
     * @return 全部的实例对象列表
     */
    <T> Set<T> setAll(String key, Class<T> clazz);

    /**
     * 判断Set中是否存在指定的value
     * Redis实现命令：sismember key value
     *
     * @param key   键
     * @param value 值
     * @return 存在返回true 不存在返回false
     */
    boolean setExist(String key, Object value);

    /**
     * 返回Set的长度
     * Redis实现命令：scard key
     *
     * @param key 键
     * @return 列表的大小 如果set不存在，返回0
     */
    long setSize(String key);

    /**
     * 随机返回Set中的一个数据
     * Redis实现命令：srandmember key
     *
     * @param key 键
     * @return 随机的值
     */
    Object setRandom(String key);

    //TODO ZSet 操作

    /**
     * 添加ZSet
     * Redis实现命令：zadd key score value
     *
     * @param key   键
     * @param value 值
     * @param score 权重
     * @return 添加成功返回true 否则返回false
     */
    boolean zsetAdd(String key, String value, double score);

    /**
     * 添加ZSet到最上面
     * Redis实现命令：zadd key 负的ZSet的长度 value
     *
     * @param key   键
     * @param value 值
     * @return 添加成功返回true 否则返回false
     */
    boolean zsetStartAdd(String key, String value);

    /**
     * 添加ZSet到最下面
     * Redis实现命令：zadd key 大于等于1的数据 value
     *
     * @param key   键
     * @param value 值
     * @return 添加成功返回true 否则返回false
     */
    boolean zsetEndAdd(String key, String value);

    /**
     * 删除ZSet值
     * Redis实现命令：zrem key value1 value2 ...
     *
     * @param key   键
     * @param value 不定数量的值
     * @return 删除后的列表大小
     */
    long zsetRemove(String key, Object... value);

    /**
     * 删除ZSet值
     * Redis实现命令：zrem key value1 value2 ...
     *
     * @param key   键
     * @param start 开始位置
     * @param end   结束位置
     * @return 删除后的列表大小
     */
    long zsetRemoveRange(String key, long start, long end);

    /**
     * 获得指定的ZSet的值
     * Redis实现命令：zrange key start end
     *
     * @param key   键
     * @param start 开始位置
     * @param end   结束位置
     * @return 从开始位置到结束位置列表的值
     */
    Set<String> zsetRange(String key, long start, long end);

    /**
     * 获得ZSet的所有的值
     * Redis实现命令：zrange key 0 -1
     *
     * @param key 键
     * @return 全部列表的值
     */
    Set<String> zsetAll(String key);

    /**
     * 获得ZSet的所有值
     * Redis实现命令：zrange key start end
     *
     * @param key   键
     * @param start 开始位置
     * @param end   结束位置
     * @param clazz 实例对象的class
     * @return 从开始位置到结束位置列表的实例对象
     */
    <T> Set<T> zsetRange(String key, long start, long end, Class<T> clazz);

    /**
     * 获得ZSet的所有值
     * Redis实现命令：zrange key 0 -1
     *
     * @param key   键
     * @param clazz 实例对象的class
     * @return 全部列表的实例对象
     */
    <T> Set<T> zsetAll(String key, Class<T> clazz);

    /**
     * 判断ZSet中是否存在指定的value
     * Redis实现命令：sismember key value
     *
     * @param key 键
     * @param value
     * @return
     */
    /*boolean zsetExist(String key, Object value) {
        return redisTemplate.opsForZSet().(key, value);
    }*/

    /**
     * 返回ZSet的长度
     * Redis实现命令：scard key
     *
     * @param key 键
     * @return 如果set不存在，返回0
     */
    long zsetSize(String key);

    /**
     * 使用Lua脚本调用redis
     *
     * @param script Lua脚本
     * @param keys   脚本中使用的KEYS
     * @param args   脚本中使用的ARGV
     * @param clazz  脚本return出信息的Class
     * @return 脚本中return出的信息
     */
    <T> T eval(String script, Class<T> clazz, List<String> keys, String... args);

}
